Computers are known to crash and/or otherwise malfunction. Often, the crash or malfunction is associated with a running computer program that corrupts a memory area in the computer's random access memory (RAM) used by another running program. Such corruption may be caused, for example, by one computer program overwriting a memory area used by another computer program. Such corruption can also be caused by malicious software, for example, a computer virus. In order to reduce the potential for memory conflicts and resulting corruption, certain conventional protection mechanisms described below, can limit the ability of a computer program to corrupt a portion of the RAM used by another computer program.
Even though it may be desirable to protect a memory area used by a computer program from corruption by another computer program, it is often advantageous for computer programs or processes to share data stored in the computer RAM memory. In some conventional computer environments, sharing is provided by copying the contents of a memory region used by a computer program to another region of RAM, which is then used by another computer program. While such copying protects the original memory region from corruption by the other computer program, the copying process is relatively slow, resulting in slow computer operation.
Conventional commercially available computer operating systems (OSs) typically provide protection of memory portions among different program processes, and between all user processes and trusted memory supervisor code. In addition, conventional operating systems typically support flexible sharing of data in computer RAM memory to allow software applications to cooperate efficiently.
Conventional computer architectures and operating systems provide a linear addressing scheme, in which each software process has a separate linear demand-paged virtual address space. Each address space has a single protection domain that defines protections to memory in the address space, shared by all threads that run within the process. With this arrangement, a software thread can only have a different protection domain if it runs in a different address space. With this arrangement, sharing is only possibly at page granularity, where a single physical memory page can be mapped into two or more virtual address spaces. A page in computer memory has a size that is typically on the order of four kilobytes. Therefore, with this arrangement, protected sharing has a relatively coarse granularity of four kilobytes.
Although the above-described addressing scheme is now common in OS designs and hardware implementations, it has significant disadvantages when used for protected sharing of memory spaces. For example, pointer-based data structures can be shared only if the shared memory region resides at the same virtual address for all participating processes. Also, all words within a page must have the same permissions. As described above, a conventional memory page has a size in the vicinity of four kilobytes, which provides memory protection regions having only a coarse memory permission granularity. Furthermore, interpretation of a pointer depends on addressing context, and any transfer of control between software modules requires a time-consuming context switch, for example, with a software call to another software module. The coarse granularity of protection regions, the time consuming overhead of providing protected memory via software calls, and the time consuming overhead of inter-process communication limits the ways in which conventional protected sharing can be used by software applications.
Although software designers are creative in working around the above limitations to implement protected memory sharing for some software applications, each software application requires considerable custom engineering effort to attain high performance.
In some cases, such as web browsers or kernel modules, software designers sacrifice memory protection robustness in favor of performance, e.g., processing speed, by foregoing hardware memory protection and placing all software modules in the same address space. It should be apparent that this arrangement can lead to computer crashes as software modules compete for the same memory space.
In contrast to systems that place all software modules in the same address space, to provide memory protection, some conventional computer architectures and operating systems, e.g., Linux on x86 or Windows XP on x86, associate each software process with its own memory address space. However, such systems can have increased complexity and run-time overhead, as described above, from managing multiple address contexts.
Some software systems benefit from an ability to provide extensibility, wherein new software modules (sometimes referred to as “plug-ins”) can be linked to existing software modules to provide enhanced functionality. Architects of these systems generally do not use conventional operating system protection support, which, as described above, provides a separate address space for each software module. Instead, the plug-in occupies the same memory space as the program to which it is linked, providing good processing speed at the expense of potential memory corruption. For example, the Apache web server has a plug-in for the interpretation of perl code in web pages. For another example, browsers can receive plug-ins to interpret portable document format (PDF) documents, a format provided by software from ADOBE® Systems Incorporated, San Jose, Calif. Linking a plug-in to an existing computer program makes communication between the computer program and the plug-in fast and flexible, but because there is no protection between the plug-in and the software program to which it is linked in the same address space, the linkage can lead to memory corruption, or open a security hole in a server (e.g., from a buffer overrun).
Embedded systems, e.g., systems having microcontrollers with embedded code, can have similar problems. Embedded systems are often organized having a set of tasks (sometimes including an operating system) that share physically addressed memory. Without inter-task memory protection, an error in part of the embedded system can make the entire embedded system unreliable. Similarly, loadable OS kernel modules (such as in Linux) all run in the kernel's unprotected address space, leading to potential reliability and security problems.
As described above, so-called “demand-paged virtual memory systems,” in order to provide multiple protection domains, can place each thread in a separate address space and then map physical memory pages to the same virtual address in each address context. These systems have a coarse protection granularity only to the memory page level.
So called “page-group systems,” such as HP-PA RISC and PowerPC, partition memory protection domains according to which page-groups (collections of memory pages) are accessible. Every protection domain that has access to a page-group sees the same permissions for all pages in the group. Page-group systems have coarse granularity corresponding to a page or multiple pages.
So called “domain-page systems” have an explicit protection domain identifier, and each protection domain can specify a permission value for each page. Permissions are managed only at page granularity.
So-called “capability systems” are an extension of segmented architectures where a “capability” is a special pointer that contains both location and protection information for a segment of memory. Although designed for protected sharing, capability systems do not function well for the common case of shared data structures that contain pointers. Capability systems do not support multiple memory protection domains, since threads sharing the data structure use its pointers (capabilities) and therefore see the same permissions for objects accessed via the shared structure. Also, many capability systems provide a relatively poor ability to revoke protection permissions, and, in order to revoke permissions, require an exhaustive sweep of the memory. Some capability systems perform an indirect lookup on each capability use, which adds considerable run-time overhead.